home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 45
/
Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso
/
Aminet
/
gfx
/
x11
/
x3270_3_2_16.lha
/
amiga_src
/
keymap.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-06-23
|
29KB
|
1,231 lines
/*
* Copyright 1996, 1999, 2000 by Paul Mattes.
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*/
/*
* keymap.c
* This module handles keymaps.
*/
#include "globals.h"
#include <X11/IntrinsicP.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/AsciiSrc.h>
#include <errno.h>
#include "appres.h"
#include "objects.h"
#include "resources.h"
#include "hostc.h"
#include "keymapc.h"
#include "keypadc.h"
#include "kybdc.h"
#include "popupsc.h"
#include "screenc.h"
#include "statusc.h"
#include "utilc.h"
#define PA_ENDL " " PA_END "()"
Boolean keymap_changed = False;
struct trans_list *trans_list = (struct trans_list *)NULL;
static struct trans_list **last_trans = &trans_list;
static struct trans_list *tkm_last;
struct trans_list *temp_keymaps; /* temporary keymap list */
char *keymap_trace = CN;
static char *last_keymap = CN;
static Boolean last_nvt = False;
static void setup_keymaps(const char *km, Boolean do_popup);
static void add_keymap(const char *name, Boolean do_popup);
static void add_trans(const char *name, char *translations, char *pathname,
Boolean is_from_server);
static char *get_file_keymap(const char *name, char **pathp);
static void keymap_3270_mode(Boolean);
/* Undocumented Xt function to convert translations to text. */
extern String _XtPrintXlations(Widget w, XtTranslations xlations,
Widget accelWidget, Boolean includeRHS);
extern Widget *screen;
#if defined(X3270_MENUS) /*[*/
extern Pixmap diamond;
extern Pixmap no_diamond;
static enum { SORT_EVENT, SORT_KEYMAP, SORT_ACTION } sort = SORT_KEYMAP;
static Boolean km_isup = False;
static Widget km_shell, sort_event, sort_keymap, sort_action, text;
static char km_file[128];
static void create_text(void);
static void km_up(Widget w, XtPointer client_data, XtPointer call_data);
static void km_down(Widget w, XtPointer client_data, XtPointer call_data);
static void km_done(Widget w, XtPointer client_data, XtPointer call_data);
static void do_sort_action(Widget w, XtPointer client_data,
XtPointer call_data);
static void do_sort_keymap(Widget w, XtPointer client_data,
XtPointer call_data);
static void do_sort_event(Widget w, XtPointer client_data, XtPointer call_data);
static void format_xlations(String s, FILE *f);
static int action_cmp(char *s1, char *s2);
static int keymap_cmp(char *k1, int l1, char *k2, int l2);
static int event_cmp(char *e1, char *e2);
static Boolean is_temp(char *k);
static char *pathname(char *k);
static Boolean from_server(char *k);
static void km_regen(void);
#else /*][*/
#define km_regen()
#endif /*]*/
/* Keymap initialization. */
void
keymap_init(const char *km)
{
static Boolean initted = False;
if (km == CN)
if ((km = (char *)getenv("KEYMAP")) == CN)
if ((km = (char *)getenv("KEYBD")) == CN)
km = "@server";
setup_keymaps(km, initted);
if (!initted) {
initted = True;
last_nvt = IN_ANSI;
register_schange(ST_3270_MODE, keymap_3270_mode);
register_schange(ST_CONNECT, keymap_3270_mode);
} else {
screen_set_keymap();
keypad_set_keymap();
}
km_regen();
/* Save the name(s) of the last keymap, so we can switch modes later. */
if (km != last_keymap) {
if (last_keymap != CN) {
Free(last_keymap);
last_keymap = CN;
}
if (km != CN)
last_keymap = NewString(km);
}
}
/*
* 3270/NVT mode change.
*/
static void
keymap_3270_mode(Boolean ignored unused)
{
if (last_nvt != IN_ANSI) {
last_nvt = IN_ANSI;
keymap_init(last_keymap);
}
}
/*
* Set up a user keymap.
*/
static void
setup_keymaps(const char *km, Boolean do_popup)
{
Boolean saw_apl_keymod = False;
struct trans_list *t;
struct trans_list *next;
/* Make sure it starts with "base". */
if (km == CN)
km = "base";
else {
char *k = XtMalloc(5 + strlen(km) + 1);
(void) sprintf(k, "base,%s", km);
km = k;
}
if (do_popup)
keymap_changed = True;
/* Clear out any existing translations. */
appres.key_map = (char *)NULL;
for (t = trans_list; t != (struct trans_list *)NULL; t = next) {
next = t->next;
XtFree(t->name);
if (t->pathname != CN)
XtFree(t->pathname);
XtFree((char *)t);
}
trans_list = (struct trans_list *)NULL;
last_trans = &trans_list;
/* Build up the new list. */
if (km != CN) {
char *ns = XtNewString(km);
char *n0 = ns;
char *comma;
do {
comma = strchr(ns, ',');
if (comma)
*comma = '\0';
if (!strcmp(ns, Apl))
saw_apl_keymod = True;
add_keymap(ns, do_popup);
if (comma)
ns = comma + 1;
else
ns = NULL;
} while (ns);
XtFree(n0);
}
if (appres.apl_mode && !saw_apl_keymod)
add_keymap(Apl, do_popup);
}
/*
* Get a keymap from a file.
*/
static char *
get_file_keymap(const char *name, char **pathp)
{
#if 0
char *home;
#endif
char *path;
XrmDatabase dd = (XrmDatabase)NULL;
char *resname;
XrmValue value;
char *type;
char *r = CN;
*pathp = CN;
#if 0
/* Look for a user's keymap file. */
home = getenv("HOME");
if (home != CN) {
path = xs_buffer("%s/.x3270/keymap.%s", home, name);
dd = XrmGetFileDatabase(path);
if (dd != (XrmDatabase)NULL)
*pathp = path;
else
XtFree(path);
}
#endif
/* Look for a global keymap file. */
if (dd == (XrmDatabase)NULL) {
path = xs_buffer("%s/keymap.%s", LIBX3270DIR, name);
dd = XrmGetFileDatabase(path);
if (dd != (XrmDatabase)NULL)
*pathp = path;
else {
XtFree(path);
return CN;
}
}
/* Look up the resource in that file. */
resname = xs_buffer("%s.%s.%s", XtName(toplevel), ResKeymap, name);
if ((XrmGetResource(dd, resname, 0, &type, &value) == True) &&
*value.addr) {
r = XtNewString(value.addr);
} else {
*pathp = CN;
}
XtFree(resname);
XrmDestroyDatabase(dd);
return r;
}
/*
* Add to the list of user-specified keymap translations, finding both the
* system and user versions of a keymap.
*/
static void
add_keymap(const char *name, Boolean do_popup)
{
char *translations, *translations_nvt;
char *buf, *buf_nvt;
int any = 0;
char *path, *path_nvt;
Boolean is_from_server = False;
if (appres.key_map == (char *)NULL)
appres.key_map = XtNewString(name);
else {
char *t = xs_buffer("%s,%s", appres.key_map, name);
XtFree(appres.key_map);
appres.key_map = t;
}
/* Translate '@server' to a vendor-specific keymap. */
if (!strcmp(name, "@server")) {
struct sk {
struct sk *next;
char *vendor;
char *keymap;
};
static struct sk *sk_list = (struct sk *)NULL;
struct sk *sk;
if (sk_list == (struct sk *)NULL) {
char *s, *vendor, *keymap;
s = get_resource("serverKeymapList");
if (s == CN)
return;
s = XtNewString(s);
while (split_dresource(&s, &vendor, &keymap) == 1) {
sk = (struct sk *)XtMalloc(sizeof(struct sk));
sk->vendor = vendor;
sk->keymap = keymap;
sk->next = sk_list;
sk_list = sk;
}
}
for (sk = sk_list; sk != (struct sk *)NULL; sk = sk->next) {
if (!strcmp(sk->vendor, ServerVendor(display))) {
name = sk->keymap;
is_from_server = True;
break;
}
}
if (sk == (struct sk *)NULL)
return;
}
/* Try for a file first, then resources. */
translations = get_file_keymap(name, &path);
buf_nvt = xs_buffer("%s.%s", name, ResNvt);
translations_nvt = get_file_keymap(buf_nvt, &path_nvt);
if (translations != CN || translations_nvt != CN) {
any++;
if (IN_ANSI && translations_nvt != CN)
add_trans(buf_nvt, translations_nvt, path_nvt,
is_from_server);
else if (translations != CN)
add_trans(name, translations, path, is_from_server);
if (translations != CN)
XtFree(translations);
if (translations_nvt != CN)
XtFree(translations_nvt);
XtFree(buf_nvt);
} else {
XtFree(buf_nvt);
/* Shared keymap. */
buf = xs_buffer("%s.%s", ResKeymap, name);
translations = get_resource(buf);
buf_nvt = xs_buffer("%s.%s.%s", ResKeymap, name, ResNvt);
translations_nvt = get_resource(buf_nvt);
if (translations != CN || translations_nvt != CN)
any++;
if (IN_ANSI && translations_nvt != CN)
add_trans(buf_nvt + strlen(ResKeymap) + 1,
translations_nvt, CN, is_from_server);
else if (translations != CN)
add_trans(name, translations, CN, is_from_server);
XtFree(buf);
XtFree(buf_nvt);
/* User keymap */
buf = xs_buffer("%s.%s.%s", ResKeymap, name, ResUser);
translations = get_resource(buf);
buf_nvt = xs_buffer("%s.%s.%s.%s", ResKeymap, name, ResNvt,
ResUser);
translations_nvt = get_resource(buf_nvt);
if (translations != CN || translations_nvt != CN)
any++;
if (IN_ANSI && translations_nvt != CN)
add_trans(buf_nvt + strlen(ResKeymap) + 1,
translations_nvt, CN, is_from_server);
else if (translations != CN)
add_trans(buf, translations, CN, is_from_server);
XtFree(buf);
XtFree(buf_nvt);
}
if (!any) {
if (do_popup)
popup_an_error("Cannot find %s \"%s\"", ResKeymap,
name);
else
xs_warning("Cannot find %s \"%s\"", ResKeymap, name);
}
}
/*
* Add a single keymap name and translation to the translation list.
*/
static void
add_trans(const char *name, char *translations, char *path_name,
Boolean is_from_server)
{
struct trans_list *t;
t = (struct trans_list *)XtMalloc(sizeof(*t));
t->name = XtNewString(name);
t->pathname = path_name;
t->is_temp = False;
t->from_server = is_from_server;
(void) lookup_tt(name, translations);
t->next = NULL;
*last_trans = t;
last_trans = &t->next;
}
/*
* Translation table expansion.
*/
/* Find the first unquoted newline an an action list. */
static char *
unquoted_newline(char *s)
{
Boolean bs = False;
enum { UQ_BASE, UQ_PLIST, UQ_Q } state = UQ_BASE;
char c;
for ( ; (c = *s); s++) {
if (bs) {
bs = False;
continue;
} else if (c == '\\') {
bs = True;
continue;
}
switch (state) {
case UQ_BASE:
if (c == '(')
state = UQ_PLIST;
else if (c == '\n')
return s;
break;
case UQ_PLIST:
if (c == ')')
state = UQ_BASE;
else if (c == '"')
state = UQ_Q;
break;
case UQ_Q:
if (c == '"')
state = UQ_PLIST;
break;
}
}
return CN;
}
/* Expand a translation table with keymap tracing calls. */
static char *
expand_table(const char *name, char *table)
{
char *cm, *t0, *t, *s;
int nlines = 1;
if (table == CN)
return CN;
/* Roughly count the number of lines in the table. */
cm = table;
while ((cm = strchr(cm, '\n')) != CN) {
nlines++;
cm++;
}
/* Allocate a new buffer. */
t = t0 = (char *)XtMalloc(2 + strlen(table) +
nlines * (strlen(" " PA_KEYMAP_TRACE "(,nnnn) ") + strlen(name) +
strlen(PA_ENDL)));
*t = '\0';
/* Expand the table into it. */
s = table;
nlines = 0;
while (*s) {
/* Skip empty lines. */
while (*s == ' ' || *s == '\t')
s++;
if (*s == '\n') {
*t++ = '\n';
*t = '\0';
s++;
continue;
}
/* Find the '>' from the event name, and copy up through it. */
cm = strchr(s, '>');
if (cm == CN) {
while ((*t++ = *s++))
;
break;
}
while (s <= cm)
*t++ = *s++;
/* Find the ':' following, and copy up throught that. */
cm = strchr(s, ':');
if (cm == CN) {
while ((*t++ = *s++))
;
break;
}
nlines++;
while (s <= cm)
*t++ = *s++;
#if defined(X3270_TRACE) /*[*/
/* Insert a PA-KeymapTrace call. */
(void) sprintf(t, " " PA_KEYMAP_TRACE "(%s,%d) ", name, nlines);
t = strchr(t, '\0');
#endif /*]*/
/*
* Copy to the next unquoted newline and append a PA-End call.
*/
cm = unquoted_newline(s);
if (cm == CN) {
while ((*t = *s)) {
t++;
s++;
}
} else {
while (s < cm)
*t++ = *s++;
}
#if defined(X3270_TRACE) /*[*/
(void) strcpy(t, PA_ENDL);
t += strlen(PA_ENDL);
#endif /*]*/
if (cm == CN)
break;
else
*t++ = *s++;
}
*t = '\0';
return t0;
}
#if defined(X3270_TRACE) /*[*/
/*
* Trace a keymap.
*
* This function leaves a value in the global "keymap_trace", which is used
* by the action_debug function when subsequent actions are called.
*/
void
PA_KeymapTrace_action(Widget w unused, XEvent *event unused, String *params,
Cardinal *num_params)
{
if (!toggled(EVENT_TRACE) || *num_params != 2)
return;
if (keymap_trace != CN)
XtFree((XtPointer)keymap_trace);
keymap_trace = XtMalloc(strlen(params[0]) + 1 + strlen(params[1]) + 1);
(void) sprintf(keymap_trace, "%s:%s", params[0], params[1]);
}
#endif /*]*/
/*
* End a keymap trace.
*
* This function clears the value in the global "keymap_trace".
*/
void
PA_End_action(Widget w unused, XEvent *event unused, String *params unused,
Cardinal *num_params unused)
{
if (keymap_trace != CN)
XtFree((XtPointer)keymap_trace);
keymap_trace = CN;
}
/*
* Translation table cache.
*/
XtTranslations
lookup_tt(const char *name, char *table)
{
struct tt_cache {
char *name;
XtTranslations trans;
struct tt_cache *next;
};
# define TTN (struct tt_cache *)NULL
static struct tt_cache *tt_cache = TTN;
struct tt_cache *t;
char *xtable;
/* Look for an old one. */
for (t = tt_cache; t != TTN; t = t->next)
if (!strcmp(name, t->name))
return t->trans;
/* Allocate and translate a new one. */
t = (struct tt_cache *)XtMalloc(sizeof(*t));
t->name = XtNewString(name);
xtable = expand_table(name, table);
t->trans = XtParseTranslationTable(xtable);
XtFree((XtPointer)xtable);
t->next = tt_cache;
tt_cache = t;
return t->trans;
}
#undef TTN
/*
* Set or clear a temporary keymap.
*
* If the parameter is CN, removes all keymaps.
* Otherwise, toggles the keymap by that name.
*
* Returns 0 if the action was successful, -1 otherwise.
*
*/
int
temporary_keymap(char *k)
{
char *kmname, *km;
XtTranslations trans;
struct trans_list *t, *prev;
char *path = CN;
# define TN (struct trans_list *)NULL
if (k == CN) {
struct trans_list *next;
/* Delete all temporary keymaps. */
for (t = temp_keymaps; t != TN; t = next) {
XtFree((XtPointer)t->name);
if (t->pathname != CN)
XtFree((XtPointer)t->pathname);
next = t->next;
XtFree((XtPointer)t);
}
tkm_last = temp_keymaps = TN;
screen_set_temp_keymap((XtTranslations)NULL);
keypad_set_temp_keymap((XtTranslations)NULL);
status_kmap(False);
km_regen();
return 0;
}
/* Check for deleting one keymap. */
for (prev = TN, t = temp_keymaps; t != TN; prev = t, t = t->next)
if (!strcmp(k, t->name))
break;
if (t != TN) {
/* Delete the keymap from the list. */
if (prev != TN)
prev->next = t->next;
else
temp_keymaps = t->next;
if (tkm_last == t)
tkm_last = prev;
XtFree((XtPointer)t->name);
XtFree((XtPointer)t);
/* Rebuild the translation tables from the remaining ones. */
screen_set_temp_keymap((XtTranslations)NULL);
keypad_set_temp_keymap((XtTranslations)NULL);
for (t = temp_keymaps; t != TN; t = t->next) {
trans = lookup_tt(t->name, CN);
screen_set_temp_keymap(trans);
keypad_set_temp_keymap(trans);
}
/* Update the status line. */
if (temp_keymaps == TN)
status_kmap(False);
km_regen();
return 0;
}
/* Add a keymap. */
/* Try a file first. */
km = get_file_keymap(k, &path);
if (km == CN) {
/* Then try a resource. */
kmname = xs_buffer("%s.%s", ResKeymap, k);
km = get_resource(kmname);
XtFree(kmname);
if (km == CN)
return -1;
}
/* Update the translation tables. */
trans = lookup_tt(k, km);
screen_set_temp_keymap(trans);
keypad_set_temp_keymap(trans);
/* Add it to the list. */
t = (struct trans_list *)XtMalloc(sizeof(*t));
t->name = XtNewString(k);
t->pathname = path;
t->is_temp = True;
t->from_server = False;
t->next = TN;
if (tkm_last != TN)
tkm_last->next = t;
else
temp_keymaps = t;
tkm_last = t;
/* Update the status line. */
status_kmap(True);
km_regen();
/* Success. */
return 0;
}
#undef TN
#if defined(X3270_MENUS) /*[*/
/* Create and pop up the current keymap pop-up. */
void
do_keymap_display(Widget w unused, XtPointer userdata unused,
XtPointer calldata unused)
{
Widget form, label, done;
/* Create the popup. */
km_shell = XtVaCreatePopupShell(
"kmPopup", transientShellWidgetClass, toplevel,
NULL);
XtAddCallback(km_shell, XtNpopupCallback, place_popup,
(XtPointer) CenterP);
XtAddCallback(km_shell, XtNpopupCallback, km_up, (XtPointer)NULL);
XtAddCallback(km_shell, XtNpopdownCallback, km_down, (XtPointer)NULL);
/* Create a form in the popup. */
form = XtVaCreateManagedWidget(
ObjDialog, formWidgetClass, km_shell,
NULL);
/* Create the title. */
label = XtVaCreateManagedWidget("label", labelWidgetClass, form,
XtNborderWidth, 0,
NULL);
/* Create the options. */
sort_event = XtVaCreateManagedWidget("sortEventOption",
commandWidgetClass, form,
XtNborderWidth, 0,
XtNfromVert, label,
XtNleftBitmap, sort == SORT_EVENT ? diamond : no_diamond,
NULL);
XtAddCallback(sort_event, XtNcallback, do_sort_event,
(XtPointer)NULL);
sort_keymap = XtVaCreateManagedWidget("sortKeymapOption",
commandWidgetClass, form,
XtNborderWidth, 0,
XtNfromVert, sort_event,
XtNleftBitmap, sort == SORT_KEYMAP ? diamond : no_diamond,
NULL);
XtAddCallback(sort_keymap, XtNcallback, do_sort_keymap,
(XtPointer)NULL);
sort_action = XtVaCreateManagedWidget("sortActionOption",
commandWidgetClass, form,
XtNborderWidth, 0,
XtNfromVert, sort_keymap,
XtNleftBitmap, sort == SORT_ACTION ? diamond : no_diamond,
NULL);
XtAddCallback(sort_action, XtNcallback, do_sort_action,
(XtPointer)NULL);
/* Create a text widget attached to the file. */
text = XtVaCreateManagedWidget(
"text", asciiTextWidgetClass, form,
XtNfromVert, sort_action,
XtNscrollHorizontal, XawtextScrollAlways,
XtNscrollVertical, XawtextScrollAlways,
XtNdisplayCaret, False,
NULL);
create_text();
/* Create the Done button. */
done = XtVaCreateManagedWidget(ObjConfirmButton,
commandWidgetClass, form,
XtNfromVert, text,
NULL);
XtAddCallback(done, XtNcallback, km_done, (XtPointer)NULL);
/* Pop it up. */
popup_popup(km_shell, XtGrabNone);
}
/* Format the keymap into a text source. */
static void
create_text(void)
{
String s;
FILE *f;
Widget source, old;
/* Ready a file. */
(void) sprintf(km_file, "/tmp/km.%d", getpid());
f = fopen(km_file, "w");
if (f == (FILE *)NULL) {
popup_an_errno(errno, "temporary file open");
return;
}
#ifndef AMIGA
s = _XtPrintXlations(*screen, (*screen)->core.tm.translations, NULL,
True);
format_xlations(s, f);
XtFree(s);
#endif
fclose(f);
source = XtVaCreateWidget(
"source", asciiSrcObjectClass, text,
XtNtype, XawAsciiFile,
XtNstring, km_file,
XtNeditType, XawtextRead,
NULL);
old = XawTextGetSource(text);
XawTextSetSource(text, source, (XawTextPosition)0);
XtDestroyWidget(old);
}
/* Refresh the keymap display, if it's up. */
static void
km_regen(void)
{
if (km_isup)
create_text();
}
/* Popup callback. */
static void
km_up(Widget w unused, XtPointer client_data unused, XtPointer call_data unused)
{
km_isup = True;
}
/* Popdown callback. Destroy the widget. */
static void
km_down(Widget w unused, XtPointer client_data unused,
XtPointer call_data unused)
{
km_isup = False;
(void) unlink(km_file);
XtDestroyWidget(km_shell);
}
/* Done button callback. Pop down the widget. */
static void
km_done(Widget w unused, XtPointer client_data unused,
XtPointer call_data unused)
{
XtPopdown(km_shell);
}
/* "Sort-by-event" button callback. */
static void
do_sort_event(Widget w unused, XtPointer client_data unused,
XtPointer call_data unused)
{
if (sort != SORT_EVENT) {
sort = SORT_EVENT;
XtVaSetValues(sort_action, XtNleftBitmap, no_diamond, NULL);
XtVaSetValues(sort_keymap, XtNleftBitmap, no_diamond, NULL);
XtVaSetValues(sort_event, XtNleftBitmap, diamond, NULL);
create_text();
}
}
/* "Sort-by-keymap" button callback. */
static void
do_sort_keymap(Widget w unused, XtPointer client_data unused,
XtPointer call_data unused)
{
if (sort != SORT_KEYMAP) {
sort = SORT_KEYMAP;
XtVaSetValues(sort_action, XtNleftBitmap, no_diamond, NULL);
XtVaSetValues(sort_keymap, XtNleftBitmap, diamond, NULL);
XtVaSetValues(sort_event, XtNleftBitmap, no_diamond, NULL);
create_text();
}
}
/* "Sort-by-action" button callback. */
static void
do_sort_action(Widget w unused, XtPointer client_data unused,
XtPointer call_data unused)
{
if (sort != SORT_ACTION) {
sort = SORT_ACTION;
XtVaSetValues(sort_action, XtNleftBitmap, diamond, NULL);
XtVaSetValues(sort_keymap, XtNleftBitmap, no_diamond, NULL);
XtVaSetValues(sort_event, XtNleftBitmap, no_diamond, NULL);
create_text();
}
}
#define DASHES \
"-------------------------- ---------------- ------------------------------------"
/*
* Format translations for display.
*
* The data from _XtPrintXlations looks like this:
* [<space>]event:<space>[PA-KeymapTrace("keymap","line")<space>][action...]
* with the delightful complication that embedded quotes are not quoted.
*
* What we want to do is to:
* remove all lines without PA-KeymapTrace
* remove the leading space
* sort by actions list
* reformat as:
* action... event (keymap:line)
*/
static void
format_xlations(String s, FILE *f)
{
char *t;
char *t_next;
struct xl {
struct xl *next;
char *actions;
char *event;
char *keymap;
int km_line;
char *full_keymap;
} *xl_list = (struct xl *)NULL, *x, *xs, *xlp, *xn;
char *km_last;
int line_last = 0;
/* Construct the list. */
for (t = s; t != CN; t = t_next) {
char *k, *a, *kk;
int nq;
static char cmps[] = ": " PA_KEYMAP_TRACE "(";
/* Find the end of this rule and terminate this line. */
t_next = strstr(t, PA_ENDL "\n");
if (t_next != CN) {
t_next += strlen(PA_ENDL);
*t_next++ = '\0';
}
/* Remove the leading space. */
while (*t == ' ')
t++;
/* Use only traced events. */
k = strstr(t, cmps);
if (k == CN)
continue;
*k = '\0';
k += strlen(cmps);
/* Find the rest of the actions. */
a = strchr(k, ')');
if (a == CN)
continue;
while (*(++a) == ' ')
;
if (!*a)
continue;
/* Remove the trailing PA-End call. */
if (strlen(a) >= strlen(PA_ENDL) &&
!strcmp(a + strlen(a) - strlen(PA_ENDL), PA_ENDL))
a[strlen(a) - strlen(PA_ENDL)] = '\0';
/* Allocate the new element. */
x = (struct xl *)XtCalloc(sizeof(struct xl), 1);
x->actions = XtNewString(a);
x->event = XtNewString(t);
x->keymap = kk = (char *)XtMalloc(a - k + 1);
x->km_line = 0;
x->full_keymap = (char *)XtMalloc(a - k + 1);
nq = 0;
while (*k != ')') {
if (*k == '"') {
nq++;
} else if (nq == 1) {
*kk++ = *k;
} else if (nq == 3) {
x->km_line = atoi(k);
break;
}
k++;
}
*kk = '\0';
(void) sprintf(x->full_keymap, "%s:%d", x->keymap, x->km_line);
/* Find where it should be inserted. */
for (xs = xl_list, xlp = (struct xl *)NULL;
xs != (struct xl *)NULL;
xlp = xs, xs = xs->next) {
int halt = 0;
switch (sort) {
case SORT_EVENT:
halt = (event_cmp(xs->event, x->event) > 0);
break;
case SORT_KEYMAP:
halt = (keymap_cmp(xs->keymap, xs->km_line,
x->keymap, x->km_line) > 0);
break;
case SORT_ACTION:
halt = (action_cmp(xs->actions, a) > 0);
break;
}
if (halt)
break;
}
/* Insert it. */
if (xlp != (struct xl *)NULL) {
x->next = xlp->next;
xlp->next = x;
} else {
x->next = xl_list;
xl_list = x;
}
}
/* Walk it. */
if (sort != SORT_KEYMAP)
(void) fprintf(f, "%-26s %-16s %s\n%s\n",
get_message("kmEvent"),
get_message("kmKeymapLine"),
get_message("kmActions"),
DASHES);
km_last = CN;
for (xs = xl_list; xs != (struct xl *)NULL; xs = xs->next) {
switch (sort) {
case SORT_EVENT:
if (km_last != CN) {
char *l;
l = strchr(xs->event, '<');
if (l != CN) {
if (strcmp(km_last, l))
(void) fprintf(f, "\n");
km_last = l;
}
} else
km_last = strchr(xs->event, '<');
break;
case SORT_KEYMAP:
if (km_last == CN || strcmp(xs->keymap, km_last)) {
char *p;
(void) fprintf(f, "%s%s '%s'%s",
km_last == CN ? "" : "\n",
get_message(is_temp(xs->keymap) ?
"kmTemporaryKeymap" :
"kmKeymap"),
xs->keymap,
from_server(xs->keymap) ?
get_message("kmFromServer") : "");
if ((p = pathname(xs->keymap)) != CN)
(void) fprintf(f, ", %s %s",
get_message("kmFile"), p);
else
(void) fprintf(f,
", %s %s.%s.%s",
get_message("kmResource"),
programname, ResKeymap, xs->keymap);
(void) fprintf(f,
"\n%-26s %-16s %s\n%s\n",
get_message("kmEvent"),
get_message("kmKeymapLine"),
get_message("kmActions"),
DASHES);
km_last = xs->keymap;
line_last = 0;
}
while (xs->km_line != ++line_last) {
(void) fprintf(f, "%-26s %s:%d\n",
get_message("kmOverridden"),
xs->keymap, line_last);
}
break;
case SORT_ACTION:
break;
}
(void) fprintf(f, "%-26s %-16s ",
xs->event, xs->full_keymap);
(void) fcatv(f, xs->actions);
(void) fputc('\n', f);
}
/* Free it. */
for (xs = xl_list; xs != (struct xl *)NULL; xs = xn) {
xn = xs->next;
XtFree((XtPointer)xs->actions);
XtFree((XtPointer)xs->event);
XtFree((XtPointer)xs->keymap);
XtFree((XtPointer)xs->full_keymap);
XtFree((XtPointer)xs);
}
}
/*
* Comparison function for actions. Basically, a strcmp() that handles "PF"
* and "PA" specially.
*/
#define PA "PA("
#define PF "PF("
static int
action_cmp(char *s1, char *s2)
{
if ((!strncmp(s1, PA, 3) && !strncmp(s2, PA, 3)) ||
(!strncmp(s1, PF, 3) && !strncmp(s2, PF, 3)))
return atoi(s1 + 4) - atoi(s2 + 4);
else
return strcmp(s1, s2);
}
/* Return a keymap's index in the lists. */
static int
km_index(char *n)
{
struct trans_list *t;
int ix = 0;
for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, n))
return ix;
ix++;
}
for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, n))
return ix;
ix++;
}
return ix;
}
/* Return whether or not a keymap is temporary. */
static Boolean
is_temp(char *k)
{
struct trans_list *t;
for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, k))
return t->is_temp;
}
for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, k))
return t->is_temp;
}
return False;
}
/* Return the pathname associated with a keymap. */
static char *
pathname(char *k)
{
struct trans_list *t;
for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, k))
return t->pathname;
}
for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, k))
return t->pathname;
}
return CN;
}
/* Return whether or not a keymap was translated from "@server". */
static Boolean
from_server(char *k)
{
struct trans_list *t;
for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, k))
return t->from_server;
}
for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
if (!strcmp(t->name, k))
return t->from_server;
}
return False;
}
/*
* Comparison function for keymaps.
*/
static int
keymap_cmp(char *k1, int l1, char *k2, int l2)
{
/* If the keymaps are the same, do a numerical comparison. */
if (!strcmp(k1, k2))
return l1 - l2;
/* The keymaps are different. Order them according to trans_list. */
return km_index(k1) - km_index(k2);
}
/*
* strcmp() that handles <KeyPress>Fnn numercally.
*/
static int
Fnn_strcmp(char *s1, char *s2)
{
static char kp[] = "<KeyPress>F";
# define KPL (sizeof(kp) - 1)
if (strncmp(s1, kp, KPL) || !isdigit(s1[KPL]) ||
strncmp(s2, kp, KPL) || !isdigit(s2[KPL]))
return strcmp(s1, s2);
return atoi(s1 + KPL) - atoi(s2 + KPL);
}
/*
* Comparison function for events.
*/
static int
event_cmp(char *e1, char *e2)
{
char *l1, *l2;
int r;
/* If either has a syntax problem, do a straight string compare. */
if ((l1 = strchr(e1, '<')) == CN ||
(l2 = strchr(e2, '<')) == CN)
return strcmp(e1, e2);
/*
* If the events are different, sort on the event only. Otherwise,
* sort on the modifier(s).
*/
r = Fnn_strcmp(l1, l2);
if (r)
return r;
else
return strcmp(e1, e2);
}
#endif /*]*/